package com.sinsz.wxpn;

import com.sinsz.common.exception.ApiException;
import com.sinsz.wxpn.support.Constant;
import com.sinsz.wxpn.util.AesException;
import com.sinsz.wxpn.util.WXBizMsgCrypt;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

/**
 * 微信服务器消息加解密器
 * @author chenjianbo
 * @date 2018-11-14
 */
public class WxpnCryptBean {

    private WXBizMsgCrypt crypt = null;

    WxpnCryptBean(String token, String appid, String encodingAesKey) {
        try {
            if (!StringUtils.isEmpty(token)
                    && !StringUtils.isEmpty(appid)
                    && !StringUtils.isEmpty(encodingAesKey)) {
                this.crypt = new WXBizMsgCrypt(token, encodingAesKey, appid);
            }
        } catch (AesException e) {
            e.printStackTrace(System.out);
        }
    }

    private void check() {
        if (crypt == null) {
            throw new ApiException(Constant.DEFAULT_ERRORCODE, "加解密实例未生效.");
        }
    }

    /**
     * 实现加密
     * @param source        xml明文内容
     * @param timestamp     10位时间戳
     * @param nonce         随机字符串
     * @return              密文内容
     */
    public String encrypt(String source, String toUser, String timestamp, String nonce) {
        check();
        try {
            String ciphertext = crypt.encryptMsg(source, timestamp, nonce);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(ciphertext);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);
            Element root = document.getDocumentElement();
            NodeList nodeText = root.getElementsByTagName("Encrypt");
            String encrypt = nodeText.item(0).getTextContent();
            String format = "<xml><ToUserName><![CDATA[%1$s]]></ToUserName><Encrypt><![CDATA[%2$s]]></Encrypt></xml>";
            return String.format(format, toUser, encrypt);
        } catch (Exception e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_ERRORCODE, "加密失败，请检查jdk的jce（开启无限制权限策略）是否配置成功.");
        }
    }

    /**
     * 实现解密
     * @param target        xml密文内容
     * @param signature     接口返回的签名参数
     * @param timestamp     时间戳
     * @param nonce         随机字符串
     * @return              明文消息对象
     */
    public String decrypt(String target, String signature, String timestamp, String nonce) {
        check();
        try {
            if (StringUtils.isEmpty(target)) {
                return "";
            }
            if (!target.contains("<Encrypt>")) {
                return target;
            }
            return crypt.decryptMsg(signature, timestamp, nonce, target);
        } catch (Exception e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_ERRORCODE, "加密失败，请检查jdk的jce（开启无限制权限策略）是否配置成功.");
        }
    }

}
